/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "Texture.h"
#include "Hardware.h"
#include <Gluck.h>
#include <logs/Log.h>

#undef max

namespace liba
{
namespace lib3d
{
namespace hard
{
namespace marmalade
{

Texture * last_texture = 0;

Texture::Texture(Hardware * hardware, int mipmap_levels, const pictureformat::Format & desired_format, int width, int height, int max_texture_size )
:	hard::Texture( mipmap_levels, desired_format, width, height ),
	texture( 0 ),
	dec_scale( 0 ),
	valid_flag( false ),
	locked_data( 0 ),
	mip_level( mipmap_levels )
{
	int texture_size = std::max( width, height );
	if( max_texture_size < texture_size )
	{
		dec_scale = (texture_size / max_texture_size) >> 1;
	}

	glGenTextures( 1, &texture );
	if( !texture )
		THROW(Gluck(__FUNCTION__, "create_internal_texture() failed"));

	//autogen mipmaps on for non compressed textures
	this->mipmap_levels = 1;
}

Texture::~Texture()
{
	valid_flag = false;

	glBindTexture( GL_TEXTURE_2D, 0 );
	last_texture = 0;

	if( texture )
		glDeleteTextures( 1, &texture );
	texture = 0;
}

void * Texture::lock(int mipmap_level, int * stride)
{
	if( mipmap_level < dec_scale )
		return 0; // Skip :)

	*stride = (width >> mipmap_level) * 4;
	
	locked_data_size = (height >> mipmap_level ) * (*stride);
	locked_data = new unsigned char [ locked_data_size ];
	return locked_data;
}

void Texture::unlock(int mipmap_level)
{
	glBindTexture( GL_TEXTURE_2D, texture );
	last_texture = this;
	
	//	unsigned char data[4] = {255, 255, 255, 255};
	//	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
	
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );

	if(mipmap_level == 0)
	{
		for(int j = 0; j < height; j++)
		{
			for(int i = original_width; i < width; i++)
				((unsigned int*)locked_data)[j * width + i] = ((unsigned int*)locked_data)[j * width + i - 1];
		}

		for(int j = 0; j < width; j++)
		{
			for(int i = original_height; i < height; i++)
				((unsigned int*)locked_data)[i * width + j] = ((unsigned int*)locked_data)[(i - 1) * width + j];
		}
	}

	glTexImage2D(GL_TEXTURE_2D, mipmap_level, GL_RGBA, width >> mipmap_level, height >> mipmap_level, 0, GL_RGBA, GL_UNSIGNED_BYTE, locked_data);

	//if(mip_level > 1 && mipmap_level == 0)
	//	glGenerateMipmapOES(GL_TEXTURE_2D);
	
	delete [] locked_data; locked_data = 0;

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );

	valid_flag = true;
}

bool Texture::is_valid()
{
	return valid_flag;
}

bool Texture::set_in_hardware(int stage)
{
	// create texture matrix
	GLfloat texture_matrix[16] = { u_scale, 0.0f,    0.0f, 0.0f,
								   0.0f,    v_scale, 0.0f, 0.0f,
								   0.0f,    0.0f,    1.0f, 0.0f,
								   0.0f,    0.0f,    0.0f, 1.0f };
	// save old matrix mode
	GLint old_mode;
	glGetIntegerv(GL_MATRIX_MODE, &old_mode);
	
	// set texture matrix
	glMatrixMode(GL_TEXTURE);
	glLoadMatrixf(texture_matrix);
	
	// reset matrix mode
	glMatrixMode(old_mode);
	
	//if(last_texture == this)
	//return true;

	glBindTexture( GL_TEXTURE_2D, texture );
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE);

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); 
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
	
	if(!filtered)
	{
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); 
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
	}
	else
	{
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	}
	
	last_texture = this;
	return true;
}

void Texture::disable(int stage)
{
	glBindTexture( GL_TEXTURE_2D, 0 );
	last_texture = 0;
}
	
void Texture::report_statistics()
{
}

} // namespace marmalade
} // namespace hard
} // namespace lib3d
} // namespace liba

